home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 9
/
Night Owl CD-ROM (NOPV9) (Night Owl Publisher) (1993).ISO
/
054a
/
execwn.zip
/
EXECWIN.PAS
< prev
Wrap
Pascal/Delphi Source File
|
1993-02-11
|
17KB
|
642 lines
{
This unit interfaces one function, ExecWindow, which uses an Exec
routine to run a child process. In addition to what the Exec routine
does, ExecWindow attempts to keep the video output of the child
process within a specified window on the screen. This is useful for
some programs, as exemplified by the INSTALL.EXE program used for
installation of Turbo Professional files.
The technique used is to grab interrupt 21h and thus control all
writes to the standard output and error devices. These are rerouted
to the screen, within the specified window. The technique used by
ExecWindow will not work for programs that write directly to video
memory, through the BIOS, or through some other file handle assigned
to the console. It does work with standard DOS commands, with the
Borland command line compilers, and with other command line utilities
like PKZIP and LHARC.
Written by Kim Kokkonen, TurboPower Software
Released to the public domain
10/09/88 - initial release
11/02/90 - update for TP6
11/30/90 - fix to work again with TP5.5
09/21/92 - update for protected mode operation under BP7
(no longer supports TP4)
01/26/93 - trap int 10 calls to support PKZIP 2.0
also fixes a problem with backspacing at DOS command line
02/11/93 - trap int 29 calls to support PKZIP 2.0 under fast ANSI drivers
}
{$R-,S-,I-,B-,F-,V-}
unit ExecWin;
{-Exec a program in a window}
interface
function ExecWindow(Command : String;
UseSecond : Boolean;
Xlo, Ylo, Xhi, Yhi : Byte;
Attr : Byte) : Integer;
{-Exec a program in a window.
Command contains the program name and command line parameters to
execute, similar to how you would enter them at the DOS command
line.
UseSecond is true if you want to execute COMMAND.COM, which will
execute your program in turn. UseSecond must be True if you want
COMMAND to search the path or if you need to use device
redirection. Note, however, that if UseSecond is True, the error
level returned by DOS.DosExitCode is that of COMMAND.COM rather
than the actual program being executed.
Xlo, Ylo, Xhi, Yhi are the window boundaries, just as for a
Window() call.
Attr is the video attribute for all writes within the window.
ExecWindow returns a DOS error code, plus the following additional
codes:
99: window coordinates invalid
$8008: insufficient DOS memory for int21 handler (DPMI only)
-1..-4: error code returned by Turbo Professional ExecDos
(real mode only)
Note that ExecWindow calls SwapVectors for you. Do not call
SwapVectors in your own program prior to calling ExecWindow.
}
{=======================================================================}
implementation
{$IFDEF DPMI} {DOS protected mode version----------------------------}
uses
Dos, WinApi, Dpmi;
const
WindLoX = 0; {Zero-relative offsets of window data in Int21 below}
WindLoY = 1;
WindHiX = 2;
WindHiY = 3;
WindPosX = 4;
WindPosY = 5;
WindAttr = 6;
Int21Patch = 9; {Zero-relative offset of Int21 patch point}
Int10Patch = 14;
Int21Entry = 18; {Zero-relative offset of actual Int21 entry point}
Int29Entry = 20;
Int10Entry = 22;
{The int handlers are copied into real mode memory before activation}
procedure IntHandlers; near; assembler;
asm
{CS-relative data}
db 0 {WindLoX}
db 0 {WindLoY}
db 0 {WindHiX}
db 0 {WindHiY}
db 0 {WindPosX}
db 0 {WindPosY}
db 0 {WindAttr}
@JmpOld21:
cli {Interrupts back off}
db $EA,0,0,0,0 {Jump to previous int21 vector}
@JmpOld10:
db $EA,0,0,0,0 {Jump to previous int10 vector}
{Actual Int21 entry point here}
jmp @OurInt21
{Actual Int29 entry point here}
jmp @TtyOut
{Actual Int10 entry point here}
cmp ah,$0E
je @TtyOut
cmp ah,$09
je @MultOut
cmp ah,$0A
je @MultOut
cmp ah,2
je @SetCurPos
jmp @JmpOld10
@OurInt21:
sti {Allow interrupts}
cmp ah,2 {Classify the DOS functions we care about}
je @DispOut
cmp ah,6
je @DirectOut
cmp ah,9
je @StringOut
cmp ah,$40
je @BlockOut
jmp @JmpOld21
@TtyOut: {BIOS function 0E}
push ax
call @WriteChar
pop ax
iret
@MultOut: {BIOS functions 09 and 0A}
push ax
push cx
@MultOut1:
call @WriteChar
loop @MultOut1
pop cx
pop ax
iret
@SetCurPos: {BIOS function 02}
push ax
push ds
push cs
pop ds {DS = CS}
cmp dl,ds:[WindLoX]
jae @SetOKCLo
mov dl,ds:[WindLoX]
@SetOKCLo:
cmp dl,ds:[WindHiX]
jbe @SetOKCHi
mov dl,ds:[WindHiX]
@SetOKCHi:
cmp dh,ds:[WindLoY]
jae @SetOKRLo
mov dh,ds:[WindLoY]
@SetOKRLo:
cmp dh,ds:[WindHiY]
jbe @SetOKRHi
mov dh,ds:[WindHiY]
@SetOKRHi:
mov ds:[WindPosX],dx
pop ds
pop ax
jmp @JmpOld10
@DirectOut: {DOS function 6}
cmp dl,$FF {Console input?}
je @JmpOld21
@DispOut: {DOS function 2}
push ax
mov al,dl
call @WriteChar
pop ax
clc
retf 2
@StringOut: {DOS function 9}
push ax
push bx
mov bx,dx
@StringOut1:
mov al,[bx] {DS:BX points to string to display}
cmp al,'$'
je @StringOut2
call @WriteChar
inc bx
jmp @StringOut1
@StringOut2:
pop bx
pop ax
clc
retf 2
@BlockOut: {DOS function $40}
cmp bx,1 {to StdOut?}
je @BlockOut1
cmp bx,2 {to StdErr?}
je @BlockOut1
jmp @JmpOld21
@BlockOut1:
jcxz @BlockOut3 {Anything to write?}
push ax
push bx
push cx
mov bx,dx
@BlockOut2:
mov al,[bx] {DS:BX points to character to display}
call @WriteChar
inc bx
loop @BlockOut2
pop bx
pop bx
pop ax
@BlockOut3:
mov ax,cx {Wrote all the bytes}
clc
retf 2
{This routine writes each character within the window}
@WriteChar:
push bp {Some int 10 handlers trash BP}
push bx
push cx
push dx
push ds
push cs
pop ds {DS = CS}
mov dx,ds:[WindPosX] {Current cursor pos in DX}
cmp al,7
je @WriteDone {Ignore bell character}
cmp al,8
je @WriteBS
cmp al,9
je @WriteTab
cmp al,10
je @WriteLF
cmp al,13
je @WriteCR
call @WriteNormal {Write normal character}
@WriteDone:
pop ds
pop dx
pop cx
pop bx
pop bp
ret
{Special case for carriage return}
@WriteCR:
mov dl,ds:[WindLoX]
call @SetCursor
jmp @WriteDone
{Special case for line feed}
@WriteLF:
cmp dh,ds:[WindHiY]
jb @WriteLF1
call @ScrollUp
jmp @WriteDone
@WriteLF1:
inc dh
call @SetCursor
jmp @WriteDone
{Special case for tab}
@WriteTab:
mov cl,dl
sub cl,ds:[WindLoX]
add cl,8
and cl,$F8
add cl,ds:[WindLoX]
sub cl,dl
xor ch,ch
@WriteTab1:
mov al,' '
push cx
call @WriteNormal
pop cx
loop @WriteTab1
jmp @WriteDone
{Special case for backspace}
@WriteBS:
cmp dl,ds:[WindLoX] {At left window edge?}
jbe @WriteDone {Done if so}
dec dl {Cursor left one}
xor bh,bh
mov ah,2 {Position cursor}
pushf
call dword ptr ds:[Int10Patch]
mov ds:[WindPosX],dx
mov cx,1
mov bl,ds:[WindAttr]
mov ax,$0920 {Write a space}
pushf
call dword ptr ds:[Int10Patch]
jmp @WriteDone
{Write normal character and scroll if needed}
@WriteNormal:
mov cx,1
mov bl,ds:[WindAttr]
xor bh,bh
mov ah,9 {Write the character}
pushf
call dword ptr ds:[Int10Patch]
cmp dl,ds:[WindHiX] {Beyond right border?}
jb @IncCol
cmp dh,ds:[WindHiY] {Room for CR/LF?}
jb @IncRow
call @ScrollUp
dec dh {Compensate for inc to follow}
@IncRow:
inc dh {Next row}
mov dl,ds:[WindLoX] {First col}
dec dl {Compensate for inc to follow}
@IncCol:
inc dl {Next column}
{Position cursor and update saved position}
@SetCursor:
xor bh,bh
mov ah,2
pushf
call dword ptr ds:[Int10Patch]
mov ds:[WindPosX],dx
ret
{Scroll window up one line}
@ScrollUp:
mov ax,$0601
mov cx,ds:[WindLoX]
mov dx,ds:[WindHiX]
mov bh,ds:[WindAttr]
pushf
call dword ptr ds:[Int10Patch]
ret
end;
{Keep immediately after IntHandlers to measure size}
procedure IntHandlersEnd; near; assembler;
asm
end;
function ExecWindow(Command : String;
UseSecond : Boolean;
Xlo, Ylo, Xhi, Yhi : Byte;
Attr : Byte) : Integer;
type
{Types mirror the data at the start of int handler above}
CoordRec =
record
case integer of
0 : (X, Y: Byte);
1 : (XY : Word)
end;
WindRec =
record
WindLo : CoordRec;
WindHi : CoordRec;
WindPos : CoordRec;
WindAttr : Byte;
end;
var
OldRealMode10 : Pointer;
OldRealMode21 : Pointer;
OldRealMode29 : Pointer;
DosMemBlock :
record
Sele, Segm : Word;
end;
Status : Integer;
BlankPos : Word;
PathName : string[127];
CommandTail : string[127];
procedure InitializeCursor(var Win : WindRec); assembler;
{-Assure cursor is in window}
asm
{Get pointer to WindRec}
les di,Win
{Get cursor pos}
mov ah,3
xor bh,bh
int $10
{Assure it's within window}
mov cx,WindRec(es:[di]).WindLo.XY
cmp dh,ch {Row above minimum?}
jae @OkXLo {Jump if so}
mov dh,ch
@OkXLo:
cmp dl,cl {Col above minimum?}
jae @OkYLo {Jump if so}
mov dl,cl
@OkYLo:
mov cx,WindRec(es:[di]).WindHi.XY
cmp dh,ch {Row below maximum?}
jbe @OkXHi {Jump if so}
mov dh,ch
@OkXHi:
cmp dl,cl {Col below maximum?}
jbe @OkYHi {Jump if so}
mov dl,cl
@OkYHi:
{Save current position}
mov WindRec(es:[di]).WindPos.XY,dx
{Position cursor}
mov ah,2
xor bh,bh
int $10
end;
function SetupHandlers : Integer;
var
Size : Word;
Win : WindRec;
begin
{Allocate a block of real mode memory}
Size := ofs(IntHandlersEnd)-ofs(IntHandlers);
LongInt(DosMemBlock) := GlobalDosAlloc(Size);
if LongInt(DosMemBlock) = 0 then begin
SetupHandlers := Integer($8008);
Exit;
end;
{Copy the int handlers to real memory}
move(@IntHandlers^, Mem[DosMemBlock.Sele:0], Size);
{Patch the old real mode vectors into the handler}
GetRealModeIntVector($21, OldRealMode21);
move(OldRealMode21, Mem[DosMemBlock.Sele:Int21Patch], 4);
GetRealModeIntVector($10, OldRealMode10);
move(OldRealMode10, Mem[DosMemBlock.Sele:Int10Patch], 4);
GetRealModeIntVector($29, OldRealMode29);
{Put window data into the handler}
with Win do begin
WindLo.X := Xlo-1;
WindLo.Y := Ylo-1;
WindHi.X := Xhi-1;
WindHi.Y := Yhi-1;
WindAttr := Attr;
end;
InitializeCursor(Win);
move(Win, Mem[DosMemBlock.Sele:0], SizeOf(WindRec));
{Activate the handlers}
SetRealModeIntVector($21, Ptr(DosMemBlock.Segm, Int21Entry));
SetRealModeIntVector($10, Ptr(DosMemBlock.Segm, Int10Entry));
SetRealModeIntVector($29, Ptr(DosMemBlock.Segm, Int29Entry));
SetupHandlers := 0;
end;
procedure ShutdownHandlers;
var
Sele : Word;
begin
SetRealModeIntVector($21, OldRealMode21);
SetRealModeIntVector($10, OldRealMode10);
SetRealModeIntVector($29, OldRealMode29);
Sele := GlobalDosFree(DosMemBlock.Sele);
end;
begin
{Validate window}
if (Xlo > Xhi) or (Ylo > Yhi) or (Xlo < 1) or (Ylo < 1) then begin
ExecWindow := 99;
Exit;
end;
{Install the int 21 handler in real mode memory}
Status := SetupHandlers;
if Status = 0 then begin
{Parse command into parts for Exec}
if Command = '' then
UseSecond := True;
CommandTail := '';
if not UseSecond then begin
{Command is assumed to be a full pathname for a program}
BlankPos := Pos(' ', Command);
if BlankPos = 0 then
PathName := Command
else begin
CommandTail := Copy(Command, BlankPos, Length(Command));
PathName := Copy(Command, 1, BlankPos-1);
end;
end else begin
{Pathname is the full pathname for COMMAND.COM}
PathName := GetEnv('COMSPEC');
if Command <> '' then
CommandTail := '/C '+Command;
end;
{Perform the exec}
SwapVectors;
Exec(PathName, CommandTail);
Status := DosError;
{Account for bug in some DPMI servers}
if Status = $4B00 then
Status := 0;
SwapVectors;
{Remove the int 21 handler from real mode memory}
ShutdownHandlers;
end;
ExecWindow := Status;
end;
{$ELSE} {DOS real mode version----------------------------------------------}
uses
Dos, OpDos;
type
ByteCast =
record
LoB, HiB : Byte;
end;
var
CurInt21 : Pointer;
CurInt10 : Pointer;
CurInt29 : Pointer;
WindPos : Word;
WindLo : Word;
WindHi : Word;
WindAttr : Byte;
{$L EXECWIN}
procedure SetCsInts; external;
procedure NewInt21; external;
procedure NewInt10; external;
procedure NewInt29; external;
{$IFDEF Ver60}
{$DEFINE Fix21}
{$ENDIF}
{$IFDEF Ver70}
{$DEFINE Fix21}
{$ENDIF}
function ExecWindow(Command : string; UseSecond : Boolean;
Xlo, Ylo, Xhi, Yhi : Byte;
Attr : Byte) : Integer;
{-Exec a program in a window}
{$IFDEF Fix21}
var
TmpInt21 : Pointer;
{$ENDIF}
begin
{Validate window}
if (Xlo > Xhi) or (Ylo > Yhi) or (Xlo < 1) or (Ylo < 1) then begin
ExecWindow := 99;
Exit;
end;
{Store global copies of window data for interrupt handler}
WindAttr := Attr;
ByteCast(WindLo).LoB := Xlo-1;
ByteCast(WindLo).HiB := Ylo-1;
ByteCast(WindHi).LoB := Xhi-1;
ByteCast(WindHi).HiB := Yhi-1;
{Assure cursor is in window}
inline
(
{;get cursor pos}
$B4/$03/ { mov ah,3}
$30/$FF/ { xor bh,bh}
$CD/$10/ { int $10}
{;assure it's within window}
$8B/$0E/>WindLo/ { mov cx,[>windlo]}
$38/$EE/ { cmp dh,ch ;row above minimum?}
$73/$02/ { jae okxlo ;jump if so}
$88/$EE/ { mov dh,ch}
{okxlo:}
$38/$CA/ { cmp dl,cl ;col above minimum?}
$73/$02/ { jae okylo ;jump if so}
$88/$CA/ { mov dl,cl}
{okylo:}
$8B/$0E/>WindHi/ { mov cx,[>windhi]}
$38/$EE/ { cmp dh,ch ;row below maximum?}
$76/$02/ { jbe okxhi ;jump if so}
$88/$EE/ { mov dh,ch}
{okxhi:}
$38/$CA/ { cmp dl,cl ;col below maximum?}
$76/$02/ { jbe okyhi ;jump if so}
$88/$CA/ { mov dl,cl}
{okyhi:}
$89/$16/>WindPos/ { mov [>windpos],dx ;save current position}
{;position cursor}
$B4/$02/ { mov ah,2}
$30/$FF/ { xor bh,bh}
$CD/$10); { int $10}
{Take over interrupt}
GetIntVec($21, CurInt21);
GetIntVec($10, CurInt10);
GetIntVec($29, CurInt29);
SetCsInts;
SetIntVec($21, @NewInt21);
SetIntVec($10, @NewInt10);
SetIntVec($29, @NewInt29);
{$IFDEF Fix21}
{Prevent SwapVectors from undoing our int21 change}
TmpInt21 := SaveInt21;
SaveInt21 := @NewInt21;
{$ENDIF}
{Exec the program}
ExecWindow := ExecDos(Command, UseSecond, NoExecDosProc);
{$IFDEF Fix21}
SaveInt21 := TmpInt21;
{$ENDIF}
{Restore interrupt}
SetIntVec($21, CurInt21);
SetIntVec($10, CurInt10);
SetIntVec($29, CurInt29);
end;
{$ENDIF}
end.